﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Main.PrimitivesCommunication;

namespace Main.Packets
{
    /// <summary>
    /// Classe abstraite qui définie la structure générale d'un paquet.
    /// </summary>
    public abstract class Packet
    {
        protected byte numeroConnexion;
        protected PacketType packetType;

        protected Packet() { }

        protected Packet(byte numeroConnexion, PacketType type)
        {
            this.numeroConnexion = numeroConnexion;
            this.packetType = type;
        }

        #region Properties

        //public Int16 Size
        //{
        //    get
        //    {
        //        Int16 retour;
        //        switch (packetType.Type)
        //        {
        //            case PacketTypes.ConnectionRequestPacket:
        //            case PacketTypes.ConnectionConfirmationPacket:
        //                retour = 4;
        //                break;
        //            case PacketTypes.FreeConnectionPacket:
        //                retour = 5;
        //                break;
        //            case PacketTypes.PositiveAcknowledgmentPacket:
        //            case PacketTypes.NegativeAcknowledgmentPacket:
        //                retour = 2;
        //                break;
        //            case PacketTypes.DataPacket:
        //                retour = Convert.ToInt16(((DataPacket)this).Data.Length);
        //                retour += 2;
        //                break;
        //            default:
        //                throw new NotImplementedException();
        //        }
        //        return retour;
        //    }
        //}

        [JsonProperty(Order = 1)]
        public byte NumeroConnexion
        {
            get { return numeroConnexion; }
            set { numeroConnexion = value; }
        }

        [JsonProperty(Order = 2)]
        public PacketType PacketType
        {
            get { return packetType; }
            set { packetType = value; }
        }

        #endregion

        /// <summary>
        /// Méthode abstraite d'envoie du paquet vers le flux passé en paramètre.
        /// </summary>
        public abstract void Send(StreamWriter sw);

        /// <summary>
        /// Méthode qui instancie un paquet reçu de la couche inférieur (liaison).
        /// Instancie le bon type de paquet selon le type de paquet.
        /// </summary>
        public static Packet Receive(StreamReader sr)
        {
            Packet p = null;
            PacketType packetType;
            string str = sr.ReadLine();
            string stringType = string.Empty;
            JObject objPacket = (JObject)JsonConvert.DeserializeObject(str);
            JProperty propsOfPacket = objPacket.Property("PacketType");
            JObject objPacketType = (JObject)JsonConvert.DeserializeObject(propsOfPacket.Value.ToString());
            foreach (JToken token in objPacketType.PropertyValues())
            {
                stringType += token.ToString();
            }
            packetType = PacketType.CreateTypeForPacket(stringType);
            switch (packetType.Type)
            {
                case PacketTypes.ConnectionRequestPacket:
                case PacketTypes.ConnectionConfirmationPacket:
                    p = ConnectionPacket.CreateConnectionPacket(str);
                    break;
                case PacketTypes.DataPacket:
                    p = DataPacket.CreateDataPacket(str);
                    break;
                case PacketTypes.NegativeAcknowledgmentPacket:
                case PacketTypes.PositiveAcknowledgmentPacket:
                    p = AcknowledgementPacket.CreateAcknowledgementPacket(str);
                    break;
                case PacketTypes.FreeConnectionPacket:
                    p = FreeConnectionPacket.CreateFreeConnectionPacket(str);
                    break;
                default:
                    throw new NotImplementedException();
            }
            return p;
        }

        public override string ToString()
        {
            return "Connexion #" + this.numeroConnexion + ", " + Enum.GetName(typeof(PacketTypes), this.packetType.Type) +" ";
        }
    }
}
